'''
Created on May 11, 2012

@author: nicole 

Contains different distance measurments, to use for instance, for Nearest Neightbor machine learning algorithm
'''

from __future__ import division
from copy import *

from numpy import *


def cosineDistance(firstuser, secounduser):
    "measures the cosine distance between fristuser and secunduser"
    
    first = [firstuser[k] for k in firstuser if k in secounduser]
    secound = [secounduser[k] for k in secounduser if k in firstuser]
    cosine =1-dot(first,secound) / (math.sqrt(dot(firstuser.values(),firstuser.values()))* math.sqrt(dot(secounduser.values(),secounduser.values())))
        
    
    return cosine



def cosineSimilarity(firstuser, secounduser):
    "measures the cosine similarity between fristuser and secunduser"
    try:
        first = [firstuser[k] for k in firstuser if k in secounduser]
        secound = [secounduser[k] for k in secounduser if k in firstuser]
        cosine =dot(first,secound) / (math.sqrt(dot(firstuser.values(),firstuser.values()))* math.sqrt(dot(secounduser.values(),secounduser.values())))
    
    except:
        print('The cosine similarity could not be calculated between:')
        print(firstuser.items())
        print(secounduser.items())
    
    return cosine


def symHammingDistance(firstuser, secounduser):
    "measures the symmetrical Hamming distance between fristuser and secounduser (eg. adds one, if one listend to a song, and the other did not)"
    try:
        hamming = sum((uservector == 0 and secound >0) or (uservector >0 and secound ==0) for secound, uservector in zip(firstuser, secounduser))
    
    except:
        print('The symmetrical hamming distance could not be calculated between:')
        print(firstuser)
        print(secounduser)
    
    return hamming

def symHammingSimilarity(firstuser, secounduser):
    "measures the symmetrical Hamming Similarity between fristuser and secounduser (eg. adds one, if one listend to a song, and the other did not)"
    try:
        hamming = sum((first == secound) or (first >0 and secound >0) for secound, first in zip(firstuser, secounduser))
    
    except:
        print('The symmetrical hamming similarity could not be calculated between:')
        print(firstuser)
        print(secounduser)
    
    return hamming


def hammingDistance(firstuser, secounduser):
    "measures the Hamming distance between fristuser and secunduser (eg. adds one, as soon as both did not listened to the same song)"
    try:
        hamming = sum((first == 0 or secound == 0) for secound, first in zip(firstuser, secounduser))

    except:
        print('The hamming distance could not be calculated between:')
        print(firstuser)
        print(secounduser)
    
    return hamming


def hammingSimilarity(firstuser, secounduser):
    "measures the Hamming similarity between fristuser and secunduser (eg. adds one, as soon as both did not listened to the same song)"
    try:
        hamming = sum((first > 0 and secound > 0) for secound, first in zip(firstuser, secounduser))

    except:
        print('The hamming similarity could not be calculated between:')
        print(firstuser)
        print(secounduser)
    
    return hamming


def euclidDistance(firstuser, secounduser):
    "measures the euclid distance between fristuser and secunduser" 
    try:
        euclid = math.sqrt(sum((first-secound)*(first-secound) for secound, first in zip(firstuser, secounduser)))

    except:
        print('The euclid distance could not be calculated between:')
        print(firstuser)
        print(secounduser)
    
    return euclid

def euclidSimilarity(firstuser, secounduser):
    "measures the euclid similarity between fristuser and secunduser" 
    try:
        euclid = 1 / math.sqrt(sum((first-secound)*(first-secound) for secound, first in zip(firstuser, secounduser)))

    except:
        print('The euclid similarity could not be calculated between:')
        print(firstuser)
        print(secounduser)
    
    return euclid

def jaccardSimilarity(firstuser, secounduser):
    "measures the jaccard similarity between fristuser and secunduser" 



    intersec = len([item for item in firstuser.keys() if secounduser.has_key(item)])
    union = len(firstuser) + len(secounduser) - intersec
    jaccard = intersec/union

    return jaccard


def jaccardSimilaritylist(firstuser, secounduser):
    "measures the jaccard similarity between fristuser and secunduser" 



    intersec = len([item for item in firstuser if item in secounduser])
    union = len(firstuser) + len(secounduser) - intersec
    jaccard = intersec/union

    return jaccard


def cosineSimilaritylist(user, median, medoidMagnitude, userMagnitude):
    "measures the cosine similarity between a user and a median"
    
    uservector = [k[1] for k in user]
    #print(uservector)
    #print(median)
    try:
        medianvector = [median[k[0]] for k in user]
    except:
        print median[0:20]
        print len(median)
        print user
        
    #print(medianvector)
    cosine =dot(uservector, medianvector) / (userMagnitude* medoidMagnitude)
    
    return cosine

def Euclidean(user, medoid):
    uservector = []
    medoidvector = []
    for tupel in user:
        uservector.append(tupel[1])
        medoidvector.append(medoid[tupel[0]])
    distance = linalg.norm(array(uservector) - array(medoidvector))
    return distance

if __name__ == '__main__':
    user = [(1,0),(2,1),(3,1)]
    median = [0.4 , 0.3, 0.2, 0.8, 0,5]
    
    print(cosineSimilaritylist(user, median))